/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-apps.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

//===========================================================================================
#include <dylab/utils/log/Log.h>
#include <dylab/utils/log/File_LogListener.h>
#include <dylab/utils/log/Console_LogListener.h>
#include <dylab/utils/FileName.h>
#include <dylab/simulation/Simulation.h>
#include <dylab/simulation/ComCollisionHandler.h>
#include <dylab/simulation/ComGfx.h>
#include <dylab/simulation/ComInput.h>
#include <dylab/simulation/ComHaptics.h>
#include <dylab/simulation/ComEventListener.h>
#include <dylab/simobject/ComModelStructure.h>
#include <dylab/simobject/ComModelStructureLoader.h>
#include <dylab/simobject/ComModelIntegrator.h>
#include <dylab/simobject/ComModelTechnique.h>

#include "GuiUpdater.h"

using namespace dylab;

//===========================================================================================
void runSimulation(const String & _simName)
{ DYLAB_TRACE_CALLS

	// create the simulation
	Simulation * simulation = new Simulation();    
	DYLAB_ASSERT(simulation != NULL);

	// load the simulation
	simulation->loadFromDefinition(_simName);

    // create gui updater object
    GuiUpdater::Instance * guiUpdater = new GuiUpdater::Instance(new GuiUpdater());
    DYLAB_ASSERT(guiUpdater != NULL);
    GuiUpdater::ScopedExLock(guiUpdater)->initialize(simulation);

    // register gui updater as a render event listener
    simulation->getGfxEventDispatcher()->registerListener(guiUpdater);

    // register gui updater as a logger listener too
	Log::getSingleton().registerListener("gfx", new GuiUpdater::LogListener(simulation));

    // log simulation statistacs
    simulation->logStatistics();    

    // main simulation loop 
	DYLAB_TRACE("Entering main simulation loop.");
    while (!simulation->getQuitFlag())
    {
        // reset stats times
        GuiUpdater::Statistics * stats;
        { stats = & dynamicCast<GuiUpdater, ComEventListener>(GuiUpdater::ScopedExLock(guiUpdater))->statistics(); }
        stats->resetStatTimes(); 

		// begin update of all simulation objects
		simulation->updateSimObjects(&stats->techniqueTime, &stats->integratorTime, &stats->collisionTime);

		// render next frame
		simulation->renderFrame(&stats->renderingTime);

		// generate events based the state of common input devices 
		simulation->captureInput(&stats->inputTime);

		// update the haptic devices
		simulation->updateHaptics(&stats->hapticsTime);

		// dispatch all generated events
		simulation->dispatchAllEvents(&stats->eventsTime);

        // update & process gfx statistics        
        {             
            ComGfx::ScopedShLock(simulation->getGfx())->getWindow()
                ->getStatistics(stats->currFPS, stats->bestFPS, stats->worstFPS, stats->triangleCount); 
            dynamicCast<GuiUpdater, ComEventListener>(GuiUpdater::ScopedExLock(guiUpdater))->processGfxStatistics();
        }

#       ifdef DYLAB_PLATFORM_WINDOWS		
            // run the message pump
			MSG msg;
			while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
				if (msg.message == WM_QUIT) // exit when window close box is clicked - NOT WORKING! (thread is running)
                {                        
					simulation->setQuitFlag();
                }
                else
                {
					TranslateMessage(&msg);
					DispatchMessage(&msg);
                }
			}		
#       endif
    }    
	DYLAB_TRACE("Leaving main simulation loop.");

    // remove simulation gfx logger
    delete Log::getSingleton().unregisterListener("gfx");

    // delete our gui updater
    delete guiUpdater;

    // delete the simulation
    delete simulation;
}

//===========================================================================================
#ifdef DYLAB_PLATFORM_WINDOWS     
#   include <windows.h>
    INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT) 
#else               
    int main(int argc, char **argv) 
#endif
{
	printf("testbed.exe  Copyright (C) 2006-2008 Lukas Krejci\n\
			This program comes with ABSOLUTELY NO WARRANTY.\n\
			This is free software, and you are welcome to redistribute it\n\
			under the terms of the GNU General Public License as published by\n\
			the Free Software Foundation, either version 3 of the License, or\n\
			any later version (see http://www.gnu.org/licenses/ for details).\n");

    try
    {   
		// create dylab log
		Log * log = new Log();    

		// register log listeners
		log->registerListener("file", new File_LogListener("dylab.log"));
		log->registerListener("console", new Console_LogListener(new Console("DyLab console")));		

		// parse command line params
		String cmds;		
        #ifdef DYLAB_PLATFORM_WINDOWS     
            if (strCmdLine != NULL)
                cmds = strCmdLine;
        #else              
            if (argc > 1)
                cmds = argv[1];
        #endif
            else            
				printf("\nUsage: tested.exe <sim. name> <entities config file> <modules config file>.");
            
		String simName, entitiesCfgFile, modulesCfgFile;
		TokenParser cmdLine(cmds, " ");
		simName = cmdLine.getTokens()[0], entitiesCfgFile = cmdLine.getTokens()[1], 
			modulesCfgFile = cmdLine.getTokens()[2];		

		// create the entity definition manager
		EntityDefinitionManager * edMgr = new EntityDefinitionManager();

		// register system entities in entity definition manager
		Simulation::registerAllEntities();
		SimObject::registerAllEntities();

		// load all entity definitions based on the given configuration file
		edMgr->loadFromConfigFile(entitiesCfgFile);

		// create component manager
		ComponentManager * compMgr = new ComponentManager();

		// register component classes in component manager
		SimObject::registerAllComponentTypes();
		Simulation::registerAllComponentTypes();

		// load the config. file with the specs. of all available components
		compMgr->load(modulesCfgFile);

        // run the simulation
		runSimulation(simName);

        // delete the component manager
        delete compMgr;

        // delete the entity definition manager
        delete edMgr;

        // delete file & console loggers
		delete log->unregisterListener("console");
        delete log->unregisterListener("file");

        // delete dylab log
        delete log;
    }
    catch (Exception & e) {
        DYLAB_LOG(e.getVerboseInfo());

        #ifdef DYLAB_PLATFORM_WINDOWS
            MessageBox(NULL, e.getVerboseInfo().c_str(), "An exception has occured", MB_OK | MB_ICONERROR | MB_TASKMODAL);
        #else
            std::cerr << "Exception occured:\n";
            std::cerr << e.getVerboseInfo() << "\n";
        #endif

        // abnormal program termination
        return 1;
    }
    catch (std::exception & e) { 
        DYLAB_LOG(e.what());

        #ifdef DYLAB_PLATFORM_WINDOWS
            String msg = typeid(e).name();
            msg += ":\n";
            msg += e.what();
            MessageBox(NULL, msg.c_str(), "An exception has occured", MB_OK | MB_ICONERROR | MB_TASKMODAL);
        #else
            std::cerr << "Exception occured (" << typeid(e).name() << "):\n";
            std::cerr << e.what() << "\n";
        #endif

        // abnormal program termination
        return 1;
    }
 
    // normal program termination
    return 0;
}